[host s3] Retrieve necessary sleep information from plain-text ACPI
authorkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Thu, 19 Jul 2007 11:53:32 +0000 (12:53 +0100)
committerkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Thu, 19 Jul 2007 11:53:32 +0000 (12:53 +0100)
tables (FADT/FACS), and keep one hypercall remained for sleep
notification.

Signed-off-by: Ke Yu <ke.yu@intel.com>
Signed-off-by: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Keir Fraser <keir@xensource.com>
xen/arch/x86/acpi/boot.c
xen/arch/x86/acpi/power.c
xen/arch/x86/acpi/suspend.c
xen/arch/x86/platform_hypercall.c
xen/drivers/acpi/tables.c
xen/include/asm-x86/acpi.h
xen/include/public/platform.h
xen/include/xen/acpi.h

index 76f329d372ea6dfb8cc573fe538bf47092f2d75f..be16765afb6d057f4705ee1b3fae28604ad7de20 100644 (file)
@@ -369,6 +369,95 @@ static int __init acpi_parse_hpet(unsigned long phys, unsigned long size)
 extern u32 pmtmr_ioport;
 #endif
 
+#ifdef CONFIG_ACPI_SLEEP
+/* Get pm1x_cnt and pm1x_evt information for ACPI sleep */
+static int __init
+acpi_fadt_parse_sleep_info(struct fadt_descriptor_rev2 *fadt)
+{
+       struct facs_descriptor_rev2 *facs = NULL;
+       uint64_t facs_pa;
+
+       if (fadt->revision >= FADT2_REVISION_ID) {
+               /* Sanity check on FADT Rev. 2 */
+               if ((fadt->xpm1a_cnt_blk.address_space_id !=
+                    ACPI_ADR_SPACE_SYSTEM_IO) ||
+                   (fadt->xpm1b_cnt_blk.address_space_id !=
+                    ACPI_ADR_SPACE_SYSTEM_IO) ||
+                   (fadt->xpm1a_evt_blk.address_space_id !=
+                    ACPI_ADR_SPACE_SYSTEM_IO) ||
+                   (fadt->xpm1b_evt_blk.address_space_id !=
+                    ACPI_ADR_SPACE_SYSTEM_IO))
+                       goto bad; 
+
+               acpi_sinfo.pm1a_cnt = (uint16_t)fadt->xpm1a_cnt_blk.address;
+               acpi_sinfo.pm1b_cnt = (uint16_t)fadt->xpm1b_cnt_blk.address;
+               acpi_sinfo.pm1a_evt = (uint16_t)fadt->xpm1a_evt_blk.address;
+               acpi_sinfo.pm1b_evt = (uint16_t)fadt->xpm1b_evt_blk.address;
+       }
+
+       if (!acpi_sinfo.pm1a_cnt)
+               acpi_sinfo.pm1a_cnt = (uint16_t)fadt->V1_pm1a_cnt_blk;
+       if (!acpi_sinfo.pm1b_cnt)
+               acpi_sinfo.pm1b_cnt = (uint16_t)fadt->V1_pm1b_cnt_blk;
+       if (!acpi_sinfo.pm1a_evt)
+               acpi_sinfo.pm1a_evt = (uint16_t)fadt->V1_pm1a_evt_blk;
+       if (!acpi_sinfo.pm1b_evt)
+               acpi_sinfo.pm1b_evt = (uint16_t)fadt->V1_pm1b_evt_blk;
+
+       /* Now FACS... */
+       if (fadt->revision >= FADT2_REVISION_ID)
+               facs_pa = fadt->xfirmware_ctrl;
+       else
+               facs_pa = (uint64_t)fadt->V1_firmware_ctrl;
+
+       facs = (struct facs_descriptor_rev2 *)
+               __acpi_map_table(facs_pa, sizeof(struct facs_descriptor_rev2));
+       if (!facs)
+               goto bad;
+
+       if (strncmp(facs->signature, "FACS", 4)) {
+               printk(KERN_ERR PREFIX "Invalid FACS signature %s\n",
+                       facs->signature);
+               goto bad;
+       }
+
+       if (facs->length < 24) {
+               printk(KERN_ERR PREFIX "Invalid FACS table length: 0x%x",
+                       facs->length);
+               goto bad;
+       }
+
+       if (facs->length < 64)
+               printk(KERN_WARNING PREFIX
+                       "FACS is shorter than ACPI spec allow: 0x%x",
+                       facs->length);
+
+       if ((acpi_rsdp_rev < 2) ||
+           (facs->length < 32)) {
+               acpi_sinfo.wakeup_vector = facs_pa + 
+                       offsetof(struct facs_descriptor_rev2,
+                                firmware_waking_vector);
+               acpi_sinfo.vector_width = 32;
+       } else {
+               acpi_sinfo.wakeup_vector = facs_pa +
+                       offsetof(struct facs_descriptor_rev2,
+                                xfirmware_waking_vector);
+               acpi_sinfo.vector_width = 64;
+       }
+
+       printk (KERN_INFO PREFIX
+               "ACPI SLEEP INFO: pm1x_cnt[%x,%x], pm1x_evt[%x,%x]\n"
+               "                 wakeup_vec[%"PRIx64"], vec_size[%x]\n",
+               acpi_sinfo.pm1a_cnt, acpi_sinfo.pm1b_cnt,
+               acpi_sinfo.pm1a_evt, acpi_sinfo.pm1b_cnt,
+               acpi_sinfo.wakeup_vector, acpi_sinfo.vector_width);
+       return 0;
+bad:
+       memset(&acpi_sinfo, 0, sizeof(acpi_sinfo));
+       return 0;
+}
+#endif
+
 static int __init acpi_parse_fadt(unsigned long phys, unsigned long size)
 {
        struct fadt_descriptor_rev2 *fadt = NULL;
@@ -413,6 +502,10 @@ static int __init acpi_parse_fadt(unsigned long phys, unsigned long size)
                printk(KERN_INFO PREFIX "PM-Timer IO Port: %#x\n",
                       pmtmr_ioport);
 #endif
+
+#ifdef CONFIG_ACPI_SLEEP
+       acpi_fadt_parse_sleep_info(fadt);
+#endif
        return 0;
 }
 
index 121327895e49f009ddf7dc5917d1aef652a8f699..54ac729a89b38668c76e5472f2ce42c3e498e835 100644 (file)
 
 #define pmprintk(_l, _f, _a...) printk(_l "<PM>" _f, ## _a )
 
-u8 sleep_states[ACPI_S_STATE_COUNT];
-DEFINE_SPINLOCK(pm_lock);
+static char opt_acpi_sleep[20];
+string_param("acpi_sleep", opt_acpi_sleep);
 
-struct acpi_sleep_info {
-    uint16_t pm1a_cnt;
-    uint16_t pm1b_cnt;
-    uint16_t pm1a_evt;
-    uint16_t pm1b_evt;
-    uint16_t pm1a_cnt_val;
-    uint16_t pm1b_cnt_val;
-    uint32_t sleep_state;
-} acpi_sinfo;
+static u8 sleep_states[ACPI_S_STATE_COUNT];
+static DEFINE_SPINLOCK(pm_lock);
 
-extern void do_suspend_lowlevel(void);
+struct acpi_sleep_info acpi_sinfo;
+
+void do_suspend_lowlevel(void);
 
 static char *acpi_states[ACPI_S_STATE_COUNT] =
 {
@@ -50,10 +45,6 @@ static char *acpi_states[ACPI_S_STATE_COUNT] =
     [ACPI_STATE_S4] = "disk",
 };
 
-unsigned long acpi_video_flags;
-unsigned long saved_videomode;
-
-/* XXX: Add suspend failure recover later */
 static int device_power_down(void)
 {
     console_suspend();
@@ -100,8 +91,27 @@ static void thaw_domains(void)
             domain_unpause(d);
 }
 
+static void acpi_sleep_prepare(u32 state)
+{
+    void *wakeup_vector_va;
+
+    if ( state != ACPI_STATE_S3 )
+        return;
+
+    wakeup_vector_va = __acpi_map_table(
+        acpi_sinfo.wakeup_vector, sizeof(uint64_t));
+    if (acpi_sinfo.vector_width == 32)
+        *(uint32_t *)wakeup_vector_va =
+            (uint32_t)bootsym_phys(wakeup_start);
+    else
+        *(uint64_t *)wakeup_vector_va =
+            (uint64_t)bootsym_phys(wakeup_start);
+}
+
+static void acpi_sleep_post(u32 state) {}
+
 /* Main interface to do xen specific suspend/resume */
-int enter_state(u32 state)
+static int enter_state(u32 state)
 {
     unsigned long flags;
     int error;
@@ -123,6 +133,8 @@ int enter_state(u32 state)
     pmprintk(XENLOG_INFO, "PM: Preparing system for %s sleep\n",
         acpi_states[state]);
 
+    acpi_sleep_prepare(state);
+
     local_irq_save(flags);
 
     if ((error = device_power_down()))
@@ -152,6 +164,8 @@ int enter_state(u32 state)
  Done:
     local_irq_restore(flags);
 
+    acpi_sleep_post(state);
+
     if ( !hvm_cpu_up() )
         BUG();
 
@@ -160,30 +174,6 @@ int enter_state(u32 state)
     return error;
 }
 
-/*
- * Xen just requires address of pm1x_cnt, and ACPI interpreter
- * is still kept in dom0. Address of xen wakeup stub will be
- * returned, and then dom0 writes that address to FACS.
- */
-int set_acpi_sleep_info(struct xenpf_set_acpi_sleep *info)
-{
-    if (acpi_sinfo.pm1a_cnt)
-        pmprintk(XENLOG_WARNING, "Multiple setting on acpi sleep info\n");
-
-    acpi_sinfo.pm1a_cnt = info->pm1a_cnt_port;
-    acpi_sinfo.pm1b_cnt = info->pm1b_cnt_port;
-    acpi_sinfo.pm1a_evt = info->pm1a_evt_port;
-    acpi_sinfo.pm1b_evt = info->pm1b_evt_port;
-    info->xen_waking_vec = (uint64_t)bootsym_phys(wakeup_start);
-
-    pmprintk(XENLOG_INFO, "pm1a[%x],pm1b[%x],pm1a_e[%x],pm1b_e[%x]"
-                       "wake[%"PRIx64"]",
-                       acpi_sinfo.pm1a_cnt, acpi_sinfo.pm1b_cnt,
-                       acpi_sinfo.pm1a_evt, acpi_sinfo.pm1b_evt,
-                       info->xen_waking_vec);
-    return 0;
-}
-
 /*
  * Dom0 issues this hypercall in place of writing pm1a_cnt. Xen then
  * takes over the control and put the system into sleep state really.
@@ -197,20 +187,23 @@ int set_acpi_sleep_info(struct xenpf_set_acpi_sleep *info)
  */
 int acpi_enter_sleep(struct xenpf_enter_acpi_sleep *sleep)
 {
-    if (!IS_PRIV(current->domain) || !acpi_sinfo.pm1a_cnt)
+    if ( !IS_PRIV(current->domain) || !acpi_sinfo.pm1a_cnt )
         return -EPERM;
 
     /* Sanity check */
-    if (acpi_sinfo.pm1b_cnt_val &&
-        ((sleep->pm1a_cnt_val ^ sleep->pm1b_cnt_val) &
-        ACPI_BITMASK_SLEEP_ENABLE))
+    if ( acpi_sinfo.pm1b_cnt_val &&
+         ((sleep->pm1a_cnt_val ^ sleep->pm1b_cnt_val) &
+          ACPI_BITMASK_SLEEP_ENABLE) )
     {
         pmprintk(XENLOG_ERR, "Mismatched pm1a/pm1b setting\n");
         return -EINVAL;
     }
 
+    if ( sleep->flags )
+        return -EINVAL;
+
     /* Write #1 */
-    if (!(sleep->pm1a_cnt_val & ACPI_BITMASK_SLEEP_ENABLE))
+    if ( !(sleep->pm1a_cnt_val & ACPI_BITMASK_SLEEP_ENABLE) )
     {
         outw((u16)sleep->pm1a_cnt_val, acpi_sinfo.pm1a_cnt);
         if (acpi_sinfo.pm1b_cnt)
@@ -222,8 +215,6 @@ int acpi_enter_sleep(struct xenpf_enter_acpi_sleep *sleep)
     acpi_sinfo.pm1a_cnt_val = sleep->pm1a_cnt_val;
     acpi_sinfo.pm1b_cnt_val = sleep->pm1b_cnt_val;
     acpi_sinfo.sleep_state = sleep->sleep_state;
-    acpi_video_flags = sleep->video_flags;
-    saved_videomode = sleep->video_mode;
 
     return enter_state(acpi_sinfo.sleep_state);
 }
@@ -247,7 +238,7 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
     outw((u16)acpi_sinfo.pm1a_cnt_val, acpi_sinfo.pm1a_cnt);
     if (acpi_sinfo.pm1b_cnt)
         outw((u16)acpi_sinfo.pm1b_cnt_val, acpi_sinfo.pm1b_cnt);
-    
+
     /* Wait until we enter sleep state, and spin until we wake */
     while (!acpi_get_wake_status());
     return_ACPI_STATUS(AE_OK);
@@ -255,12 +246,24 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
 
 static int __init acpi_sleep_init(void)
 {
-    int i = 0; 
+    int i;
+    char *p = opt_acpi_sleep;
+
+    while ( (p != NULL) && (*p != '\0') )
+    {
+        if ( !strncmp(p, "s3_bios", 7) )
+            acpi_video_flags |= 1;
+        if ( !strncmp(p, "s3_mode", 7) )
+            acpi_video_flags |= 2;
+        p = strchr(p, ',');
+        if ( p != NULL )
+            p += strspn(p, ", \t");
+    }
 
     pmprintk(XENLOG_INFO, "ACPI (supports");
-    for (i = 0; i < ACPI_S_STATE_COUNT; i++)
+    for ( i = 0; i < ACPI_S_STATE_COUNT; i++ )
     {
-        if (i == ACPI_STATE_S3)
+        if ( i == ACPI_STATE_S3 )
         {
             sleep_states[i] = 1;
             printk(" S%d", i);
@@ -269,6 +272,7 @@ static int __init acpi_sleep_init(void)
             sleep_states[i] = 0;
     }
     printk(")\n");
+
     return 0;
 }
 __initcall(acpi_sleep_init);
index 2825c076e785058f02bd70728bfda0012c887385..812d644e9f4619c2f222bab5330deea2df340cba 100644 (file)
@@ -35,9 +35,6 @@ void save_rest_processor_state(void)
     rdmsrl(MSR_CSTAR, saved_cstar);
     rdmsrl(MSR_LSTAR, saved_lstar);
 #endif
-
-    bootsym(video_flags) = acpi_video_flags;
-    bootsym(video_mode) = saved_videomode;
 }
 
 #define loaddebug(_v,_reg) \
index 485d093547fd489cbfeb313bc22b0b90f78cd142..6d1f019343aa0cff55a65be5da99fc62eeeff63d 100644 (file)
@@ -248,21 +248,9 @@ ret_t do_platform_op(XEN_GUEST_HANDLE(xen_platform_op_t) u_xenpf_op)
         }
         break;
 
-#if 0
-    case XENPF_set_acpi_sleep:
-    {
-        ret = set_acpi_sleep_info(&op->u.set_acpi_sleep);
-        if (!ret && copy_to_guest(u_xenpf_op, op, 1))
-            ret = -EFAULT;
-    }
-    break;
-
     case XENPF_enter_acpi_sleep:
-    {
         ret = acpi_enter_sleep(&op->u.enter_acpi_sleep);
-    }
-    break;
-#endif
+        break;
 
     default:
         ret = -ENOSYS;
index 313e7e5320b4379d349b9310fb3a009d1b5666e9..2a38116d1559a53c28ea76d9495ab78886993fee 100644 (file)
@@ -73,6 +73,7 @@ struct acpi_table_sdt {
 
 static unsigned long sdt_pa;   /* Physical Address */
 static unsigned long sdt_count;        /* Table count */
+unsigned char acpi_rsdp_rev;
 
 static struct acpi_table_sdt sdt_entry[ACPI_MAX_TABLES] __initdata;
 
@@ -598,6 +599,8 @@ int __init acpi_table_init(void)
               "RSDP (v%3.3d %6.6s                                ) @ 0x%p\n",
               rsdp->revision, rsdp->oem_id, (void *)rsdp_phys);
 
+       acpi_rsdp_rev = rsdp->revision;
+
        if (rsdp->revision < 2)
                result =
                    acpi_table_compute_checksum(rsdp,
index 1e3a873b9404f325379d15218be1a866d1627b4b..b72c009227640ad3310a80131a82c6cad5d4f714 100644 (file)
@@ -173,15 +173,25 @@ extern unsigned long acpi_wakeup_address;
 /* early initialization routine */
 extern void acpi_reserve_bootmem(void);
 
-extern unsigned long acpi_video_flags;
-extern unsigned long saved_videomode;
-struct xenpf_set_acpi_sleep;
+extern struct acpi_sleep_info acpi_sinfo;
+#define acpi_video_flags bootsym(video_flags)
 struct xenpf_enter_acpi_sleep;
-extern int set_acpi_sleep_info(struct xenpf_set_acpi_sleep *info);
 extern int acpi_enter_sleep(struct xenpf_enter_acpi_sleep *sleep);
 extern int acpi_enter_state(u32 state);
 
-#endif /*CONFIG_ACPI_SLEEP*/
+struct acpi_sleep_info {
+    uint16_t pm1a_cnt;
+    uint16_t pm1b_cnt;
+    uint16_t pm1a_evt;
+    uint16_t pm1b_evt;
+    uint16_t pm1a_cnt_val;
+    uint16_t pm1b_cnt_val;
+    uint32_t sleep_state;
+    uint64_t wakeup_vector;
+    uint32_t vector_width;
+};
+
+#endif /* CONFIG_ACPI_SLEEP */
 
 extern u8 x86_acpiid_to_apicid[];
 #define MAX_LOCAL_APIC 256
index 0e60fadbe5b287b0e9163c57cdd67ce41b82a168..d1a44835de0813a05f36b8f09b6ebeeace7a08e7 100644 (file)
@@ -153,27 +153,13 @@ struct xenpf_firmware_info {
 typedef struct xenpf_firmware_info xenpf_firmware_info_t;
 DEFINE_XEN_GUEST_HANDLE(xenpf_firmware_info_t);
 
-#define XENPF_set_acpi_sleep      51
-struct xenpf_set_acpi_sleep {
-    /* IN variables. */
-    uint16_t pm1a_cnt_port;
-    uint16_t pm1b_cnt_port;
-    uint16_t pm1a_evt_port;
-    uint16_t pm1b_evt_port;
-    /* OUT variables */
-    uint64_t xen_waking_vec;   /* Tell dom0 to set FACS waking vector */
-};
-typedef struct xenpf_set_acpi_sleep xenpf_set_acpi_sleep_t;
-DEFINE_XEN_GUEST_HANDLE(xenpf_set_acpi_sleep_t);
-
-#define XENPF_enter_acpi_sleep    52
+#define XENPF_enter_acpi_sleep    51
 struct xenpf_enter_acpi_sleep {
     /* IN variables */
-    uint16_t pm1a_cnt_val;
-    uint16_t pm1b_cnt_val;
-    uint32_t sleep_state;       /* Which state to enter */
-    uint32_t video_flags;       /* S3_bios or s3_mode */
-    uint32_t video_mode;        /* Mode setting for s3_mode */
+    uint16_t pm1a_cnt_val;      /* PM1a control value. */
+    uint16_t pm1b_cnt_val;      /* PM1b control value. */
+    uint32_t sleep_state;       /* Which state to enter (Sn). */
+    uint32_t flags;             /* Must be zero. */
 };
 typedef struct xenpf_enter_acpi_sleep xenpf_enter_acpi_sleep_t;
 DEFINE_XEN_GUEST_HANDLE(xenpf_enter_acpi_sleep_t);
@@ -189,7 +175,6 @@ struct xen_platform_op {
         struct xenpf_microcode_update  microcode;
         struct xenpf_platform_quirk    platform_quirk;
         struct xenpf_firmware_info     firmware_info;
-        struct xenpf_set_acpi_sleep    set_acpi_sleep;
         struct xenpf_enter_acpi_sleep  enter_acpi_sleep;
         uint8_t                        pad[128];
     } u;
index b84f047651d91cb3a5bcb1b10e116f9306da5e57..3482d913914aa8a1fd36af922c3b547d9bf6256e 100644 (file)
@@ -534,5 +534,6 @@ static inline int acpi_get_pxm(acpi_handle handle)
 #endif
 
 extern int pnpacpi_disabled;
+extern unsigned char acpi_rsdp_rev;
 
 #endif /*_LINUX_ACPI_H*/